home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / mail / delivery / smail-3.002 / smail-3
Text File  |  1994-12-13  |  53KB  |  1,686 lines

  1.           SMTP Extensions for Smail 3.1.29.1
  2.                   Version 2
  3.         by Simon Leinen <simon@lia.di.epfl.ch>
  4.  
  5.                  DESCRIPTION
  6.                  -----------
  7. This file contains a patch to the Smail 3.1.29.1 sources that
  8. implements RFCs 1425, 1426 and 1427 as well as the Internet Draft
  9. `draft-ietf-smtpext-pipeline-02.txt'.  The following features are
  10. implemented:
  11.  
  12. * The new EHLO command is recognized as mandated by [RFC1425].  The
  13.   list of supported extensions in the reply includes HELP, EXPN and
  14.   optionally 8BITMIME and SIZE.  If a message is received from a
  15.   remote mailer that said EHLO, the Received: line that is added to
  16.   the message will contain a sender protocol of "esmtp" rather than
  17.   "stmp" (or "ebsmtp" rather than "bsmtp").
  18.  
  19. * The PIPELINING extension is provided by the server (this did not
  20.   necessiatate any modifications of how the server works) and used by
  21.   the client.  The PIPELINING extension reduces the number of
  22.   turn-arounds in SMTP conversations between two mailers that both
  23.   support it.  This can be a big win over slow IP networks.  The
  24.   implementation is based on the Internet Draft
  25.   `draft-ietf-smtpext-pipeline-02.txt' which is available from many
  26.   archive sites that have IETF documents.
  27.  
  28. * BODY [RFC1426] and SIZE [RFC1427] clauses are recognized in MAIL
  29.   FROM: SMTP commands on the receiver side.  Currently it doesn't
  30.   matter whether the BODY type is 7BIT or 8BITMIME - Smail is always
  31.   8-bit transparent.  If the value passed in a SIZE clause exceeds
  32.   max_message_size, a "552 message too large" error reply is
  33.   generated.
  34.  
  35. * The sender side tries to greet the receiving SMTP with an EHLO
  36.   command.  If this fails, we send the standard HELO greeting.  If
  37.   this also fails, we try RSET to clear up the remote mailer and then
  38.   HELO.  If the EHLO is accepted, Smail parses the response to find
  39.   out whether the receiver understands SIZE and BODY.  A maximum
  40.   message size that is provided by the receiving SMTP is currently
  41.   ignored, but the MAIL FROM: command is decorated with a SIZE
  42.   declaration based on a guess of the spooled message's size.  The
  43.   BODY clause is never issued.
  44.  
  45. * If HAVE_DF_SPOOL is defined, Smail will try to dynamically find out
  46.   how much space is available for spooling incoming files.  The
  47.   announced SIZE will be limited by available disk space in addition
  48.   to the administrative limit imposed by max_message_size.  The code
  49.   that computes free disk space needs either the statvfs() or the
  50.   statfs() call.  HAVE_STATVFS should be defined on systems that have
  51.   statvfs().  If your system doesn't have statvfs(), HAVE_SYS_STATFS_H
  52.   should be defined if you have the <sys/statfs.h> header.
  53.  
  54. * The SMTP greeting line (smtp_banner) now has a different default
  55.   value when HAVE_EHLO is defined.  The new greeting is two lines
  56.   long, and the second line starts with the string "ESMTP".  It seems
  57.   that this is necessary to activate ESMTP in a sending SMTP agent
  58.   that is based on Sendmail 8.  The two-line greeting (although
  59.   perfectly legal according to the RFCs) *might* cause problems with
  60.   legacy mailers.  Fortunately I haven't found such a case yet.  In
  61.   case you have problems with other mailers not being able to connect
  62.   to yours, you can always override the `smtp_banner' variable in one
  63.   of Smail's configuration files.  I would also be interested in
  64.   hearing about such broken mailers if they exist.
  65.  
  66. * NEW (December 13, 1994): The XVRB, XONE and XQUE extensions are
  67.   recognized and logged.  Some Sendmail variants support these.  Only
  68.   the XVRB extension is used when debugging, in order to get more
  69.   messages from a receiving sendmail.
  70.  
  71. * NEW (December 13, 1994): When the receiving SMTP is broken so that
  72.   it drops the line after we have sent EHLO, then the connection will
  73.   now be retried immediately without ESMTP negotiation.  This case
  74.   used to be interpreted as a normal line failure, so that messages to
  75.   such mailers would be retried for a long time and eventually bounced.
  76.  
  77.                  INSTALLATION
  78.                  ------------
  79. Apply the enclosed patch to the top-level directory of the Smail
  80. distribution (the one that contains the src/ subdirectory).  You will
  81. need a fairly recent version of the "patch" program because the patch
  82. is in the newer, more compact and more readable Unidiff format.
  83.  
  84. The server side of the extensions cannot be disabled in this version.
  85. This means that EHLO is always accepted as an SMTP greeting, and the
  86. SIZE parameter is accepted.
  87.  
  88. In order to enable the extensions, the following symbols can be added
  89. to the HAVE variable in the EDITME file:
  90.  
  91.     EHLO    enables client-side support for EHLO, SIZE and
  92.         PIPELINING
  93.     ESMTP_8BITMIME
  94.         to enable minimal server-side RFC 1426 support.
  95.         Basically the BODY clause is accepted and ignored.
  96.     DF_SPOOL
  97.         causes free disk space to be checked dynamically when
  98.         determining the maximum size of messages that will be
  99.         accepted.  This needs other symbols such as
  100.         HAVE_STATVFS or HAVE_SYS_STAT_H to be defined in turn.
  101.  
  102.                    CAVEATS
  103.                    -------
  104. When an ESMTP-capable client Smail talks to a non-ESMTP-capable SMTP
  105. server, there are two additional query/reply pairs for each SMTP
  106. connection.  This is because Smail issues EHLO to find out whether the
  107. recipient mailer handles ESMTP, gets an error message and then sends
  108. an RSET before retrying with HELO.  If you have extremely slow or
  109. unreliable SMTP connections, or if your network connections are
  110. charged by the packet this may matter to you.
  111.  
  112.                  TODO
  113.                  ----
  114. Right now the SMTP extensions don't really give any new functionality,
  115. except that max_message_size is *somewhat* enforced, but only for
  116. cooperating (RFC 1427-savvy) client mailers.  The following
  117. improvements to the code might be useful:
  118.  
  119. * Smail should be extended to handle 8-bit text messages in a more
  120.   standard way.  Currently it is simply 8-bit transparent, which is
  121.   seen as a feature by most users.  However, sending non-7-bit-ASCII
  122.   characters to remote mailer daemons is a violation of RFC 821 and
  123.   can lead to interoperability problems.  Even when communicating over
  124.   8-bit clean SMTP networks, the "Just-Send-8" approach has the
  125.   problem that it is not clear which character set has been used by
  126.   the author [RFC1428].
  127.  
  128.   In principle, all these problems are solved by the MIME standard
  129.   [RFC1341, RFC1342], which is rapidly gaining acceptance.  In a world
  130.   where everybody uses MIME-capable mailers, there wouldn't be any
  131.   eighth bits to be transported.  But as long as people still send
  132.   8-bit messages, Smail could convert such messages to 7bit-MIME when
  133.   they are transported over an SMTP network.  RFC1428 describes how
  134.   this could be done.
  135.  
  136. * If the "link broken by EHLO" problem really does occur in the real
  137.   world, it has to be handled by Smail's SMTP client side.  For
  138.   instance, once an EHLO command causes a link to be broken, the
  139.   receiving host should be noted in a "black list" by a mechanism
  140.   similar to the retry file mechanism.  Hosts should be removed from
  141.   the black list periodically, so that when they are upgraded to
  142.   SMTP-conforming software, the new functionality will be used.  It
  143.   would also be nice if the "retry" file could be extended by
  144.   something like "ehlo/noehlo" keyword that would allow specific
  145.   hosts/domains to be excluded from ESMTP trials.
  146.  
  147.                   ReFerenCes
  148.  
  149. 1344  N. Borenstein, "Implications of MIME for Internet Mail Gateways",
  150.       06/11/1992. (Pages=9) (Format=.txt, .ps)
  151.  
  152. 1425  J. Klensin, N. Freed, M. Rose, E. Stefferud, D. Crocker, "SMTP
  153.       Service Extensions", 02/10/1993. (Pages=10) (Format=.txt)
  154.  
  155. 1426  J. Klensin, N. Freed, M. Rose, E. Stefferud, D. Crocker, "SMTP
  156.       Service Extension for 8bit-MIMEtransport", 02/10/1993. (Pages=6)
  157.       (Format=.txt)
  158.       
  159. 1427  K. Moore, N. Freed, J. Klensin, "SMTP Service Extension for
  160.       Message Size Declaration", 02/10/1993. (Pages=8) (Format=.txt)
  161.       
  162. 1428  G. Vaudreuil, "Transition of Internet Mail from Just-Send-8 to
  163.       8Bit-SMTP/MIME", 02/10/1993. (Pages=6) (Format=.txt)
  164.       
  165. 1521  N. Borenstein, N. Freed, "MIME (Multipurpose Internet Mail
  166.       Extensions) Part One: Mechanisms for Specifying and Describing
  167.       the Format of Internet Message Bodies", 09/23/1993. (Pages=81)
  168.       (Format=.txt) (Obsoletes RFC1341)
  169.  
  170. 1522  K. Moore, "MIME (Multipurpose Internet Mail Extensions) Part Two:
  171.       Message Header Extensions for Non-ASCII Text",
  172.       09/23/1993. (Pages=10) (Format=.txt) (Obsoletes RFC1342)
  173.  
  174. --- src/Makefile    1994/11/14 09:19:33    1.1
  175. +++ src/Makefile    1994/11/29 18:30:03
  176. @@ -1,5 +1,5 @@
  177.  #!/bin/make -f
  178. -# @(#) $Id: Makefile,v 1.1 1994/11/14 09:19:33 logiciel Exp $
  179. +# @(#) $Id: Makefile,v 1.2 1994/11/29 18:30:03 logiciel Exp $
  180.  # Makefile for the smail program
  181.  #
  182.  #    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
  183. @@ -167,6 +167,10 @@
  184.          (cd $$i; ${MK} SRC_PREFIX=${SRC_PREFIX}$$i/ depend); \
  185.       done
  186.  
  187. +iobpeek.h: ${DEFS_SH}
  188. +    @rm -f iobpeek.h
  189. +    @. ./${DEFS_SH}; ${XEXEC} ${SHELL} geniobpeek.sh $$CC "$$CFLAGS"
  190. +
  191.  depend: ${SRC} check_defs subdir_depend local_depend
  192.  
  193.  subdir_depend: remove_driver_makefiles \
  194. @@ -303,5 +307,6 @@
  195.  
  196.  # special dependency:
  197.  version.o: version.h
  198. +smtprecv.o: iobpeek.h
  199.  
  200.  # DO NOT REMOVE THIS LINE, OR "make depend" WILL NOT WORK
  201. --- src/addr.c    1994/11/14 09:20:09    1.1
  202. +++ src/addr.c    1994/11/29 18:31:32
  203. @@ -1,4 +1,4 @@
  204. -/* @(#) $Id: addr.c,v 1.1 1994/11/14 09:20:09 logiciel Exp $ */
  205. +/* @(#) $Id: addr.c,v 1.2 1994/11/29 18:31:31 logiciel Exp $ */
  206.  
  207.  /*
  208.   *    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
  209. @@ -63,6 +63,16 @@
  210.      char *address;            /* address to be preparsed */
  211.      char **error;            /* return error message here */
  212.  {
  213. +    char * ignore;
  214. +    return preparse_address_1(address, error, & ignore);
  215. +}
  216. +
  217. +char *
  218. +preparse_address_1(address, error, rest)
  219. +    char *address;
  220. +    char **error;
  221. +    char **rest;
  222. +{
  223.      register char *ap;            /* temp for scanning address */
  224.      char *mark_start = NULL;        /* marked position of < */
  225.      char *mark_end = NULL;        /* marked position of > */
  226. @@ -125,9 +135,10 @@
  227.      ap = xmalloc((unsigned)(strlen(address) + 1));
  228.      (void) strcpy(ap, address);
  229.      if (mark_end) {
  230. -        *mark_end = '>';        /* widden the original address */
  231. +        *mark_end++ = '>';        /* widden the original address */
  232.      }
  233.      DEBUG1(DBG_ADDR_HI, "preparse_address returns: %s\n", ap);
  234. +    *rest = mark_end;
  235.      return ap;            /*  no transformations */
  236.      }
  237.  
  238. @@ -144,9 +155,10 @@
  239.          ap = xmalloc((unsigned)(strlen(address) + 1));
  240.          (void) strcpy(ap, address);
  241.          if (mark_end) {
  242. -        *mark_end = '>';    /* widden the original address */
  243. +        *mark_end++ = '>';    /* widden the original address */
  244.          }
  245.          DEBUG1(DBG_ADDR_HI, "preparse address returns: %s\n", ap);
  246. +        *rest = mark_end;
  247.          return ap;        /* address should be okay */
  248.      }
  249.      ap++;
  250. @@ -157,7 +169,7 @@
  251.          /* first part already !-route */
  252.          (void) strncpy(p, address, ap-address);
  253.          if (mark_end) {
  254. -        *mark_end = '>';    /* widden the original address */
  255. +        *mark_end++ = '>';    /* widden the original address */
  256.          }
  257.          ap = build_uucp_route(ap, error); /* build !-route */
  258.          if (ap == NULL) {
  259. @@ -167,15 +179,17 @@
  260.          (void) strcat(p, ap);    /* concatenate together */
  261.          xfree(ap);
  262.          DEBUG1(DBG_ADDR_HI, "preparse_address returns: %s\n", p);
  263. +        *rest = mark_end;
  264.          return p;            /* transformed */
  265.      }
  266.      }
  267.      ap = xmalloc((unsigned)(strlen(address) + 1));
  268.      (void) strcpy(ap, address);
  269.      if (mark_end) {
  270. -    *mark_end = '>';    /* widden the original address */
  271. +    *mark_end++ = '>';    /* widden the original address */
  272.      }
  273.      DEBUG1(DBG_ADDR_HI, "preparse address returns: %s\n", ap);
  274. +    *rest = mark_end;
  275.      return ap;                /* no transformations */
  276.  }
  277.  
  278. --- src/config.h    1994/11/14 09:53:56    1.1
  279. +++ src/config.h    1994/11/29 18:32:51
  280. @@ -1,4 +1,4 @@
  281. -/* @(#) $Id: config.h,v 1.1 1994/11/14 09:53:56 logiciel Exp $ */
  282. +/* @(#) $Id: config.h,v 1.2 1994/11/29 18:32:50 logiciel Exp $ */
  283.  
  284.  /*
  285.   *    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
  286. @@ -663,7 +663,11 @@
  287.   * SMTP startup banner message
  288.   */
  289.  #ifndef SMTP_BANNER
  290. -# define SMTP_BANNER "$primary_name Smail$version #$compile_num ready at $date"
  291. +# ifdef HAVE_EHLO
  292. +#  define SMTP_BANNER "$primary_name Smail$version #$compile_num ready at $date\nESMTP supported"
  293. +# else
  294. +#  define SMTP_BANNER "$primary_name Smail$version #$compile_num ready at $date"
  295. +# endif
  296.  #endif
  297.  
  298.  /*
  299. @@ -939,7 +943,11 @@
  300.  # ifdef    SMALL_MEMORY
  301.  #  define MESSAGE_BUF_SIZE    BUFSIZ
  302.  # else
  303. -#  define MESSAGE_BUF_SIZE    MAX_MESSAGE_SIZE
  304. +#  if MAX_MESSAGE_SIZE == 0
  305. +#   define MESSAGE_BUF_SIZE    (256*1024)
  306. +#  else
  307. +#   define MESSAGE_BUF_SIZE    MAX_MESSAGE_SIZE
  308. +#  endif
  309.  # endif
  310.  #endif
  311.  
  312. --- src/extern.h    1994/11/14 09:23:47    1.1
  313. +++ src/extern.h    1994/11/29 18:33:22
  314. @@ -1,4 +1,4 @@
  315. -/* @(#) $Id: extern.h,v 1.1 1994/11/14 09:23:47 logiciel Exp $ */
  316. +/* @(#) $Id: extern.h,v 1.2 1994/11/29 18:33:22 logiciel Exp $ */
  317.  
  318.  /*
  319.   *    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
  320. @@ -237,6 +237,7 @@
  321.   */
  322.  /* external functions defined in addr.c */
  323.  extern char *preparse_address();
  324. +extern char *preparse_address_1();
  325.  extern int parse_address();
  326.  extern char *address_token();
  327.  extern char *back_address_token();
  328. @@ -460,6 +461,7 @@
  329.  extern int new_grade();
  330.  extern void defer_message();
  331.  extern long message_date();
  332. +extern long spool_max_free_space();
  333.  
  334.  /* external functions defined in string.c */
  335.  extern int strcmpic();
  336. --- src/smtprecv.c    1994/11/14 09:24:09    1.1
  337. +++ src/smtprecv.c    1994/11/30 15:10:35
  338. @@ -1,4 +1,4 @@
  339. -/* @(#) $Id: smtprecv.c,v 1.1 1994/11/14 09:24:09 logiciel Exp $ */
  340. +/* @(#) $Id: smtprecv.c,v 1.3 1994/11/30 15:10:34 logiciel Exp $ */
  341.  
  342.  /*
  343.   *    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
  344. @@ -22,6 +22,7 @@
  345.  #include "dys.h"
  346.  #include "log.h"
  347.  #include "hash.h"
  348. +#include "iobpeek.h"
  349.  #ifndef DEPEND
  350.  # include "extern.h"
  351.  # include "debug.h"
  352. @@ -45,6 +46,7 @@
  353.  /* types local to this file */
  354.  enum e_smtp_commands {
  355.      HELO_CMD,                /* HELO domain */
  356. +    EHLO_CMD,                /* EHLO domain */
  357.      MAIL_CMD,                /* MAIL FROM:<sender> */
  358.      RCPT_CMD,                /* RCPT TO:<recipient> */
  359.      DATA_CMD,                /* DATA */
  360. @@ -60,15 +62,33 @@
  361.  };
  362.  
  363.  /* functions local to this file */
  364. +#ifdef __STDC__
  365. +static void reset_state(void);
  366. +static enum e_smtp_commands read_smtp_command(FILE *, FILE *);
  367. +static void expand_addr(char *, FILE *);
  368. +static int verify_addr(char *, FILE *, int);
  369. +static void smtp_input_signals(void);
  370. +static void smtp_processing_signals(void);
  371. +static void set_term_signal(int);
  372. +static void smtp_receive_timeout_sig(int);
  373. +static void smtp_sig_unlink(int);
  374. +#ifdef HAVE_DF_SPOOL
  375. +static long compute_max_message_size_from_df_spool (void);
  376. +#endif
  377. +#else /* not __STDC__ */
  378.  static void reset_state();
  379.  static enum e_smtp_commands read_smtp_command();
  380.  static void expand_addr();
  381. -static void verify_addr();
  382. +static int verify_addr();
  383.  static void smtp_input_signals();
  384.  static void smtp_processing_signals();
  385.  static void set_term_signal();
  386.  static void smtp_receive_timeout_sig();
  387.  static void smtp_sig_unlink();
  388. +#ifdef HAVE_DF_SPOOL
  389. +static long compute_max_message_size_from_df_spool ();
  390. +#endif
  391. +#endif /* not __STDC__ */
  392.  
  393.  /* variables local to this file */
  394.  static char *data;            /* interesting data within input */
  395. @@ -80,6 +100,7 @@
  396.      "250-The following SMTP commands are recognized:",
  397.      "250-",
  398.      "250-   HELO hostname                   - startup and give your hostname",
  399. +    "250-   EHLO hostname                   - startup with extension info",
  400.      "250-   MAIL FROM:<sender-address>      - start transaction from sender",
  401.      "250-   RCPT TO:<recipient-address>     - name recipient for message",
  402.      "250-   VRFY <address>                  - verify deliverability of address",
  403. @@ -98,6 +119,15 @@
  404.      "250 Multiple messages may be specified.  End the last one with a QUIT."
  405.  };
  406.  
  407. +typedef enum {
  408. +    BF_unspecified,
  409. +    BF_7bit,
  410. +    BF_8bitmime
  411. +}
  412. +BodyFormat;
  413. +
  414. +#define BS_UNSPECIFIED -1L
  415. +typedef long BodySize;
  416.  
  417.  
  418.  /*
  419. @@ -132,6 +162,9 @@
  420.      FILE *save_errfile = errfile;
  421.      int save_debug = debug;
  422.      int temp, i, c;
  423. +    char *rest;
  424. +    int ehlo_p = 0;
  425. +    long accepted_msg_size = max_message_size;
  426.  
  427.      /* initialize state */
  428.      initialize_state();
  429. @@ -205,11 +238,14 @@
  430.      if (out) {
  431.          alarm(smtp_receive_command_timeout);
  432.      }
  433. -    switch (read_smtp_command(in)) {
  434. +    switch (read_smtp_command(in, out)) {
  435. +    case EHLO_CMD:
  436. +        ehlo_p = 1;
  437.      case HELO_CMD:
  438.          strip_rfc822_comments(data);
  439.          if (out && data[0] == '\0') {
  440. -        fprintf(out, "501 HELO requires domain name as operand\r\n");
  441. +        fprintf(out, "501 %s requires domain name as operand\r\n",
  442. +            ehlo_p ? "EHLO" : "HELO");
  443.          fflush(out);
  444.          break;
  445.          }
  446. @@ -217,38 +253,74 @@
  447.          sender_host = COPY_STRING(data);
  448.          }
  449.          if (sender_proto == NULL) {
  450. -        sender_proto = (out? "smtp": "bsmtp");
  451. +        sender_proto = (out? ehlo_p? "esmtp": "smtp"
  452. +                : ehlo_p? "ebsmtp": "bsmtp");
  453.          }
  454. +        if (! ehlo_p) {
  455.          if (out) {
  456.          fprintf(out, "250 %s Hello %s\r\n", primary_name, data);
  457.          fflush(out);
  458.          }
  459.          reset_state();
  460.          break;
  461. +        }
  462. +        strip_rfc822_comments(data);
  463. +        if (out && data[0] == '\0') {
  464. +        fprintf(out, "501 EHLO requires domain name as operand\r\n");
  465. +        fflush(out);
  466. +        break;
  467. +        }
  468. +        if (sender_host == NULL && data[0] != '\0') {
  469. +        sender_host = COPY_STRING(data);
  470. +        }
  471. +        if (sender_proto == NULL) {
  472. +        sender_proto = (out? "smtp": "bsmtp");
  473. +        }
  474. +        if (out) {
  475. +        fprintf(out, "250-%s Hello %s, here's what we support:\r\n",
  476. +            primary_name, data);
  477. +#ifndef NO_VERIFY
  478. +        fprintf(out, "250-EXPN\r\n");
  479. +#endif
  480. +#ifdef HAVE_DF_SPOOL
  481. +        accepted_msg_size = compute_max_message_size_from_df_spool ();
  482. +        if (accepted_msg_size == -1 
  483. +            || (max_message_size && max_message_size < accepted_msg_size))
  484. +            accepted_msg_size = max_message_size;
  485. +#endif
  486. +        if (accepted_msg_size && accepted_msg_size != -1) {
  487. +            fprintf(out, "250-SIZE %d\r\n", accepted_msg_size);
  488. +        } else {
  489. +            fprintf(out, "250-SIZE\r\n");
  490. +        }
  491. +#ifdef HAVE_ESMTP_8BITMIME
  492. +        fprintf(out, "250-8BITMIME\r\n");
  493. +#endif
  494. +        fprintf(out, "250-PIPELINING\r\n");
  495. +        fprintf(out, "250 HELP\r\n");
  496. +        fflush(out);
  497. +        }
  498. +        reset_state();
  499. +        break;
  500.  
  501.      case MAIL_CMD:
  502.          strip_rfc822_comments(data);
  503.          if (out && data[0] == '\0') {
  504.          fprintf(out, "501 MAIL FROM requires address as operand\r\n");
  505. -        fflush(out);
  506.          break;
  507.          }
  508.          if (sender) {
  509.          if (out) {
  510.              fprintf(out, "503 Sender already specified\r\n");
  511. -            fflush(out);
  512.          }
  513.          break;
  514.          }
  515. -        sender = preparse_address(data, &error);
  516. +        sender = preparse_address_1(data, &error, &rest);
  517.          if (out) {
  518. -        if (sender) {
  519. -            fprintf(out, "250 <%s> ... Sender Okay\r\n",
  520. -                   sender);
  521. -        } else {
  522. +        if (!sender) {
  523.              fprintf(out, "501 <%s> ... %s\r\n", data, error);
  524. +            break;
  525.          }
  526. -        fflush(out);
  527.          }
  528.          if (sender && sender[0] == '\0') {
  529.          /* special error sender form <> given */
  530. @@ -258,26 +330,143 @@
  531.          /* special smail-internal <+> was given */
  532.          sender = COPY_STRING("<+>");
  533.          }
  534. +        {
  535. +        char * format = 0;
  536. +        int format_length = 0;
  537. +        BodyFormat body_format = BF_unspecified;
  538. +        BodySize body_size = BS_UNSPECIFIED;
  539. +
  540. +        while (rest && *rest != '\0') {
  541. +            /* maybe we have an extended MAIL command */
  542. +            while (*rest != '\0' && isspace (*rest)) {
  543. +            ++rest;
  544. +            }
  545. +            {
  546. +            int restlen = 0;
  547. +            while (*(rest+restlen) != 0
  548. +                   && !isspace (*(rest+restlen))
  549. +                   && *(rest+restlen) != '=') {
  550. +                ++restlen;
  551. +            }
  552. +            if (strncmpic(rest, "SIZE", restlen) == 0) {
  553. +                rest += restlen;
  554. +                if (*rest != '=') {
  555. +                if (out) {
  556. +                    *(rest+restlen) = '\0';
  557. +                    fprintf(out, "555 missing SIZE parameter\r\n",
  558. +                        rest);
  559. +                }
  560. +                goto fail_mail_cmd;
  561. +                }
  562. +                ++rest;
  563. +                restlen = 0;
  564. +                body_size = 0;
  565. +                while (*(rest+restlen) != 0
  566. +                   && isdigit (*(rest+restlen))
  567. +                   && (!accepted_msg_size || body_size <= accepted_msg_size)) {
  568. +                body_size = 10*body_size+*(rest+restlen)-'0';
  569. +                ++restlen;
  570. +                }
  571. +                if (accepted_msg_size && body_size > accepted_msg_size) {
  572. +                if (out) {
  573. +                    fprintf(out, "552 message too large\r\n");
  574. +                }
  575. +                goto fail_mail_cmd;
  576. +                } else if (*(rest+restlen) != 0
  577. +                       && !isspace (*(rest+restlen))) {
  578. +                if (out) {
  579. +                    while (*(rest+restlen) != 0
  580. +                       && !isspace (*(rest+restlen))) {
  581. +                    ++restlen;
  582. +                    }
  583. +                    *(rest+restlen) = '\0';
  584. +                    fprintf(out, "555 malformed SIZE clause %s\r\n",
  585. +                        rest);
  586. +                }
  587. +                goto fail_mail_cmd;
  588. +                }
  589. +            } else
  590. +#ifdef HAVE_ESMTP_8BITMIME
  591. +            if (strncmpic(rest, "BODY", restlen) == 0) {
  592. +                rest += restlen;
  593. +                if (*rest != '=') {
  594. +                if (out) {
  595. +                    *(rest+restlen) = '\0';
  596. +                    fprintf(out, "555 missing BODY parameter\r\n",
  597. +                        rest);
  598. +                }
  599. +                goto fail_mail_cmd;
  600. +                }
  601. +                ++rest;
  602. +                restlen = 0;
  603. +                while (*(rest+restlen) != 0
  604. +                   && !isspace (*(rest+restlen))) {
  605. +                ++restlen;
  606. +                }
  607. +                if (strncmpic(rest, "7BIT", restlen) == 0) {
  608. +                body_format = BF_7bit;
  609. +                } else if (strncmpic(rest, "8BITMIME", restlen) == 0) {
  610. +                body_format = BF_8bitmime;
  611. +                } else {
  612. +                if (out) {
  613. +                    *(rest+restlen) = '\0';
  614. +                    fprintf(out, "555 unknown BODY type %s\r\n",
  615. +                        rest);
  616. +                }
  617. +                goto fail_mail_cmd;
  618. +                }
  619. +            } else 
  620. +#endif
  621. +            {
  622. +                if (out) {
  623. +                *(rest+restlen) = '\0';
  624. +                fprintf(out, "555 Unknown MAIL TO: option %s\r\n", rest);
  625. +                }
  626. +                goto fail_mail_cmd;
  627. +            }
  628. +            rest += restlen;
  629. +            }
  630. +        }
  631. +        if (out) {
  632. +            if (format && *format != '\0') {
  633. +            *(format+format_length) = '\0';
  634. +            fprintf(out, "250 <%s> ... Sender Okay, using format %s\r\n",
  635. +                sender, format);
  636. +            } else {
  637. +            fprintf(out, "250 <%s> ... Sender Okay\r\n",
  638. +                sender);
  639. +            }
  640. +        }
  641. +        }
  642. +        break;
  643. +      fail_mail_cmd:
  644. +        if (sender) {
  645. +        xfree(sender);
  646. +        sender = NULL;
  647. +        }
  648.          break;
  649.  
  650.      case RCPT_CMD:
  651.          strip_rfc822_comments(data);
  652.          if (out && data[0] == '\0') {
  653.          fprintf(out, "501 RCPT TO requires address as operand\r\n");
  654. -        fflush(out);
  655.          break;
  656.          }
  657.          cur = alloc_addr();
  658.          if (out) {
  659. +#ifdef VERIFY_RCPTS
  660. +        if (! verify_addr (data, out, 1)) {
  661. +            break;
  662. +        }
  663. +#else /* not VERIFY_RCPTS */
  664.          if (cur->work_addr = preparse_address(data, &error)) {
  665.              fprintf(out, "250 <%s> ... Recipient Okay\r\n",
  666.                  cur->work_addr);
  667. -            fflush(out);
  668.          } else {
  669.              fprintf(out, "501 <%s> ... %s\r\n", data, error);
  670. -            fflush(out);
  671.              break;
  672.          }
  673. +#endif
  674.          }
  675.          /*
  676.           * surround in angle brackets, if the addr begins with `-'.
  677. @@ -404,7 +593,7 @@
  678.          fprintf(out, "502 Command not implemented\r\n");
  679.  #else
  680.          strip_rfc822_comments(data);
  681. -        verify_addr(data, out);
  682. +        verify_addr(data, out, 0);
  683.          fflush(out);
  684.  #endif
  685.          }
  686. @@ -438,7 +627,6 @@
  687.          reset_state();
  688.          if (out) {
  689.          fprintf(out, "250 Reset state\r\n");
  690. -        fflush(out);
  691.          }
  692.          break;
  693.  
  694. @@ -549,18 +737,22 @@
  695.  }
  696.  
  697.  static enum e_smtp_commands
  698. -read_smtp_command(f)
  699. +read_smtp_command(f, out)
  700.      register FILE *f;            /* SMTP command stream */
  701. +    register FILE *out;            /* output, may have to be flushed */
  702.  {
  703.      static struct str input;        /* buffer storing recent command */
  704.      static int inited = FALSE;        /* TRUE if input initialized */
  705.      register int c;            /* input char */
  706. +    int flushed_p = !out;
  707. +
  708.      static struct smtp_cmd_list {
  709.      char *name;
  710.      int len;
  711.      enum e_smtp_commands cmd;
  712.      } smtp_cmd_list[] = {
  713.      "HELO",     sizeof("HELO")-1,    HELO_CMD,
  714. +    "EHLO",     sizeof("EHLO")-1,    EHLO_CMD,
  715.      "MAIL FROM:",    sizeof("MAIL FROM:")-1,    MAIL_CMD,
  716.      "RCPT TO:",    sizeof("RCPT TO:")-1,    RCPT_CMD,
  717.      "DATA",        sizeof("DATA")-1,    DATA_CMD,
  718. @@ -580,7 +772,15 @@
  719.      } else {
  720.      input.i = 0;
  721.      }
  722. -    while ((c = getc(f)) != '\n' && c != EOF) {
  723. +    for (;;) {
  724. +    if (!flushed_p && IOB_MAYBE_EMPTY_P (f)) {
  725. +        ++flushed_p;
  726. +        fflush (out);
  727. +    }
  728. +    c = getc(f);
  729. +    if (c == EOF || c == '\n') {
  730. +        break;
  731. +    }
  732.      STR_NEXT(&input, c);
  733.      }
  734.      if (input.p[input.i - 1] == '\r') {
  735. @@ -665,24 +865,26 @@
  736.   *
  737.   * redisplay the input address if it is a valid address.
  738.   */
  739. -static void
  740. -verify_addr(in_addr, out)
  741. +static int
  742. +verify_addr(in_addr, out, rcpt_p)
  743.      char *in_addr;            /* input address string */
  744.      FILE *out;                /* write expansion here */
  745. +    int rcpt_p;                /* non-zero if called from RCPT */
  746.  {
  747.      struct addr *addr = alloc_addr();    /* get an addr structure */
  748.      struct addr *okay = NULL;        /* verified address */
  749.      struct addr *defer = NULL;        /* temporarily unverifiable addr */
  750.      struct addr *fail = NULL;        /* unverified addr */
  751.      char *error;            /* hold error message */
  752. +    int error_code;            /* reply code for negative result */
  753.  
  754. +    error_code = rcpt_p ? 501 : 550;
  755.      addr->in_addr = in_addr;        /* setup the input addr structure */
  756.      /* build the mungeable addr string */
  757.      addr->work_addr = preparse_address(in_addr, &error);
  758.      if (addr->work_addr == NULL) {
  759. -    fprintf(out, "501 %s ... %s\r\n", in_addr, error);
  760. -    fflush(out);
  761. -    return;
  762. +    fprintf(out, "%d %s ... %s\r\n", error_code, in_addr, error);
  763. +    return 0;
  764.      }
  765.  
  766.      /* cache directors and routers on the assumption we will need them again */
  767. @@ -698,15 +900,20 @@
  768.  
  769.      if (okay) {
  770.      fprintf(out, "250 %s\r\n", in_addr);
  771. +    return 1;
  772.      } else if (defer) {
  773. -    fprintf(out, "550 %s ... cannot verify: %s\r\n", in_addr,
  774. -        defer->error->message);
  775. +    fprintf(out, "%d %s ... cannot verify: %s\r\n", 
  776. +        rcpt_p ? 250 : 550, in_addr, defer->error->message);
  777. +    return 1;
  778.      } else if (fail) {
  779. -    fprintf(out, "550 %s ... not matched: %s\r\n", in_addr,
  780. -        fail->error->message);
  781. +    fprintf(out, "%d %s ... not matched: %s\r\n",
  782. +        error_code, in_addr, fail->error->message);
  783. +    return 0;
  784.      } else {
  785.      /* hmmm, it should have been in one of the lists */
  786. -    fprintf(out, "550 %s ... not matched\r\n", in_addr);
  787. +    fprintf(out, "%d %s ... not matched\r\n",
  788. +        error_code, in_addr);
  789. +    return 0;
  790.      }
  791.  }
  792.  #endif    /* NO_VERIFY */
  793. @@ -806,3 +1013,17 @@
  794.      unlink_spool();
  795.      exit(EX_OSFILE);
  796.  }
  797. +
  798. +#ifdef HAVE_DF_SPOOL
  799. +static long
  800. +compute_max_message_size_from_df_spool (void)
  801. +{
  802. +    long free_bytes = spool_max_free_space ();
  803. +    const long reserved = 2*1024*1024;
  804. +    const long min_max_message_size = 20*1024;
  805. +
  806. +    if (free_bytes == -1)
  807. +    return free_bytes;
  808. +    return free_bytes < 2*reserved ? -1 : free_bytes - reserved;
  809. +}
  810. +#endif
  811. --- src/spool.c    1994/11/14 16:53:48    1.1
  812. +++ src/spool.c    1994/11/30 08:45:19
  813. @@ -1,4 +1,4 @@
  814. -/* @(#) $Id: spool.c,v 1.1 1994/11/14 16:53:48 logiciel Exp $ */
  815. +/* @(#) $Id: spool.c,v 1.2 1994/11/30 08:45:18 logiciel Exp $ */
  816.  
  817.  /*
  818.   *    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
  819. @@ -1622,6 +1622,63 @@
  820.      return clock;
  821.  }
  822.  
  823. +
  824. +#ifdef HAVE_DF_SPOOL
  825. +#ifdef HAVE_STATVFS
  826. +#include <sys/statvfs.h>
  827. +#else /* not HAVE_STATVFS */
  828. +#ifdef HAVE_SYS_STATFS_H
  829. +#include <sys/statfs.h>
  830. +#else /* not HAVE_SYS_STATFS_H */
  831. +#include <sys/vfs.h>
  832. +#endif /* not HAVE_SYS_STATFS_H */
  833. +#endif /* not HAVE_STATVFS */
  834. +
  835. +long
  836. +spool_max_free_space ()
  837. +{
  838. +    char *dirs = spool_dirs;
  839. +    char dirname[SIZE_FILNAM];
  840. +    char *p;
  841. +    long max_free_bytes = -1, free_bytes;
  842. +    int result;
  843. +#ifdef HAVE_STATVFS
  844. +    struct statvfs buf;
  845. +#else
  846. +    struct statfs buf;
  847. +#endif
  848. +
  849. +    while (dirs && *dirs) {
  850. +    for (p = dirname; *dirs && *dirs != ':'; *p++ = *dirs++) ;
  851. +    if (*dirs == ':') {
  852. +        /* next try gets the next directory */
  853. +        dirs++;
  854. +    }
  855. +    *p++ = '\0';            /* terminate directory name */
  856. +#ifdef HAVE_STATVFS
  857. +    result = statvfs (dirname, &buf);
  858. +#else
  859. +    result = statfs (dirname, &buf, sizeof buf, 0);
  860. +#endif
  861. +    if (result == -1)
  862. +        continue;
  863. +#ifdef HAVE_STATVFS
  864. +    free_bytes = buf.f_bfree * buf.f_frsize;
  865. +#else
  866. +    free_bytes = buf.f_bfree * buf.f_bsize;
  867. +#endif
  868. +    if (free_bytes > max_free_bytes)
  869. +        max_free_bytes = free_bytes;
  870. +    }
  871. +    return max_free_bytes;
  872. +}
  873. +#else /* not HAVE_DF_SPOOL */
  874. +long
  875. +spool_max_free_space ()
  876. +{
  877. +    return -1;
  878. +}
  879. +#endif /* not HAVE_DF_SPOOL */
  880.  
  881.  #ifdef STANDALONE
  882.  
  883. --- src/transports/smtplib.c    1994/11/14 09:32:24    1.1
  884. +++ src/transports/smtplib.c    1994/12/13 11:39:29
  885. @@ -1,4 +1,4 @@
  886. -/* @(#) $Id: smtplib.c,v 1.1 1994/11/14 09:32:24 logiciel Exp $ */
  887. +/* @(#) $Id: smtplib.c,v 1.5 1994/12/13 11:39:28 logiciel Exp $ */
  888.  
  889.  /*
  890.   *    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
  891. @@ -30,6 +30,9 @@
  892.  # include "../error.h"
  893.  # include "../debug.h"
  894.  #endif
  895. +#if !defined(NO_LOG_EHLO)
  896. +#  include "../log.h"
  897. +#endif
  898.  
  899.  #ifdef ANSI_C
  900.  # define P_(x)  x
  901. @@ -40,6 +43,7 @@
  902.  #endif
  903.  
  904.  /* supported SMTP commands */
  905. +#define EHLO(domain)    "EHLO %s", domain
  906.  #define HELO(domain)    "HELO %s", domain
  907.  #define MAIL_BEGIN    "MAIL FROM:<"
  908.  #define MAIL_END    ">"
  909. @@ -48,8 +52,10 @@
  910.  #define DATA        "DATA"
  911.  #define DATA_END    "."
  912.  #define QUIT        "QUIT"
  913. +#define VERB        "VERB"
  914.  
  915.  /* reply code groups, encoded in hex */
  916. +#define POSITIVE_DEBUG        0x000    /* debugging messages */
  917.  #define POSITIVE_PRELIM        0x100    /* positive preliminary replies */
  918.  #define POSITIVE_COMPLETE    0x200    /* positive completion replies */
  919.  #define POSITIVE_INTERMEDIATE    0x300    /* positive intermediate replies */
  920. @@ -69,6 +75,8 @@
  921.  /* pseudo-reply codes */
  922.  #define REPLY_PROTO_ERROR    0x498    /* protocol error on read */
  923.  #define REPLY_TIMEOUT        0x499    /* timeout on read, or EOF on read */
  924. +#define REPLY_NOT_ACCEPTABLE    0x501    /* request not acceptable */
  925. +#define REPLY_SEQUENCE_ERROR    0x503    /* Bad sequence of commands */
  926.  
  927.  /* variables local to this file */
  928.  static struct str smtp_out;        /* region for outgoing commands */
  929. @@ -77,18 +85,45 @@
  930.  static JUMP_ENVBUF timeout_buf;        /* timeouts jump here */
  931.  
  932.  /* functions local to this file */
  933. +#ifdef __STDC__
  934. +extern int smtp_startup(struct smtp *, struct error **, int);
  935. +extern int smtp_send(struct smtp *, struct addr *, struct addr **, struct addr **, struct addr **, struct error **);
  936. +extern void smtp_shutdown(struct smtp *);
  937. +static void do_smtp_shutdown(struct smtp *, int);
  938. +static int write_command_maybe_wait(struct smtp *, unsigned, char *, int, char **);
  939. +static int wait_write_command(struct smtp *, unsigned, char *, int, char **);
  940. +static int write_command_nowait(struct smtp *, char *, int);
  941. +static int flush_command_stream(struct smtp *, char **);
  942. +static int wait_read_response(struct smtp *, unsigned, char **);
  943. +static int read_response_internal(struct smtp *, unsigned, char **);
  944. +static void catch_timeout(void);
  945. +static struct error * no_remote(struct transport *, char *);
  946. +static struct error * try_again(struct transport *, char *);
  947. +static struct error * fatal_error(struct transport *, char *);
  948. +static struct error * remote_full(struct transport *, char *);
  949. +static struct error * remote_bad_address(struct transport *, char *);
  950. +static struct error * write_failed(struct transport *);
  951. +static struct error * read_failed(struct transport *); 
  952. +#else /* not __STDC__ */
  953. +extern int smtp_startup();
  954. +extern int smtp_send();
  955. +extern void smtp_shutdown();
  956.  static void do_smtp_shutdown();
  957. +static int write_command_maybe_wait();
  958.  static int wait_write_command();
  959. +static int write_command_nowait();
  960. +static int flush_command_stream();
  961.  static int wait_read_response();
  962. +static int read_response_internal();
  963.  static void catch_timeout();
  964. -static struct error *no_remote();
  965. -static struct error *try_again();
  966. -static struct error *fatal_error();
  967. -static struct error *remote_full();
  968. -static struct error *remote_bad_address();
  969. -static struct error *write_failed();
  970. -static struct error *read_failed();
  971. -
  972. +static struct error * no_remote();
  973. +static struct error * try_again();
  974. +static struct error * fatal_error();
  975. +static struct error * remote_full();
  976. +static struct error * remote_bad_address();
  977. +static struct error * write_failed();
  978. +static struct error * read_failed(); 
  979. +#endif /* not __STDC__ */
  980.  
  981.  /*
  982.   * smtp_startup - initiate contact on an SMTP connection
  983. @@ -97,20 +132,31 @@
  984.   * the session for future mail commands.  Once the startup has been
  985.   * acomplished, smtp_send() can be used to send individual messages.
  986.   *
  987. + * If try_ehlo is non-zero and ESMTP support is configured, perform
  988. + * ESMTP negotiaition using the EHLO greeting command.
  989. + *
  990.   * return:
  991.   *    SMTP_SUCCEED on successful startup
  992.   *    SMTP_FAIL if the connection should not be retried
  993.   *    SMTP_AGAIN if the connection should be retried later
  994. + *    SMTP_EHLO_FAIL if the receiver hung up in response
  995. + *        to an EHLO command.  This can only occur when
  996. + *        try_ehlo is set.
  997.   *
  998. - * For SMTP_FAIL and SMTP_AGAIN, return a filled-in error structure.
  999. + * For SMTP_FAIL and SMTP_AGAIN (but not SMTP_EHLO_FAIL), return a
  1000. + * filled-in error structure.
  1001.   */
  1002.  int
  1003. -smtp_startup(smtpb, error_p)
  1004. +smtp_startup(smtpb, error_p, try_ehlo)
  1005.      struct smtp *smtpb;            /* SMTP description block */
  1006.      struct error **error_p;        /* error description */
  1007. +    int try_ehlo;            /* whether to use ESMTP */
  1008.  {
  1009.      int reply;
  1010.      char *reply_text;
  1011. +#ifdef HAVE_EHLO
  1012. +    int tried_rset = 0;
  1013. +#endif
  1014.  
  1015.      if (! smtp_init_flag) {
  1016.      STR_INIT(&smtp_in);
  1017. @@ -134,6 +180,134 @@
  1018.      return SMTP_AGAIN;
  1019.      }
  1020.  
  1021. +#ifdef HAVE_EHLO
  1022. +    if (try_ehlo) {
  1023. +    /*
  1024. +     * say who we are.
  1025. +     * Possible responses:
  1026. +     *    250 - okay        (continue conversation)
  1027. +     *    421 - closing down  (try again later)
  1028. +     *  5xx - fatal error   (try HELO)
  1029. +     */
  1030. +    smtp_out.i = 0;
  1031. +    (void) str_printf(&smtp_out, EHLO(primary_name));
  1032. +
  1033. +    reply = wait_write_command(smtpb, smtpb->short_timeout,
  1034. +                   smtp_out.p, smtp_out.i, &reply_text);
  1035. +
  1036. +    if (reply == REPLY_TIMEOUT) {
  1037. +        /* Some gateways just terminate the conection when they
  1038. +           receive an EHLO.  We handle this case by returning a
  1039. +           special value here.  Also write a message to the log
  1040. +           file because it is interesting to know which mailers
  1041. +           expose this problem. */
  1042. +        write_log(LOG_SYS, "link broken by EHLO!");
  1043. +        return SMTP_EHLO_FAIL;
  1044. +    }
  1045. +    if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
  1046. +        /* remote SMTP closed, try again later */
  1047. +        *error_p = try_again(smtpb->tp, reply_text);
  1048. +        return SMTP_AGAIN;
  1049. +    }
  1050. +    if (reply == REPLY_OK) {
  1051. +        char * cp = reply_text;
  1052. +        int on_greet_line = 1;
  1053. +
  1054. +        smtpb->smtp_flags = ESMTP_basic;
  1055. +        /* Parse the EHLO reply to find out
  1056. +           what the remote server supports */
  1057. +        while (*cp != 0) {
  1058. +        int skip;
  1059. +        int keywordlength;
  1060. +
  1061. +        for (skip = 4; *cp != 0 && skip; --skip, ++cp) {
  1062. +            if (skip == 1
  1063. +            ? (*cp != ' ' && *cp != '-')
  1064. +            : (! isdigit (*cp))) {
  1065. +            goto malformed_ehlo_reply;
  1066. +            }
  1067. +        }
  1068. +        if (skip != 0) {
  1069. +            goto malformed_ehlo_reply;
  1070. +        }
  1071. +        if (on_greet_line) {
  1072. +            /*
  1073. +             * Ignore greeting on first line
  1074. +             */
  1075. +            on_greet_line = 0;
  1076. +            goto skip_rest_of_line;
  1077. +        }
  1078. +        for (keywordlength = 0;
  1079. +             *(cp+keywordlength) != 0
  1080. +             && !isspace (*(cp+keywordlength));
  1081. +             ++keywordlength)
  1082. +            ;
  1083. +        if (strncmpic(cp, "SIZE", keywordlength) == 0) {
  1084. +            unsigned long max_size = 0;
  1085. +
  1086. +            cp += keywordlength;
  1087. +            while (*cp == ' ' || *cp == '\t')
  1088. +            ++cp;
  1089. +            if (!isdigit(*cp) && *cp != '\n')
  1090. +            goto malformed_ehlo_reply;
  1091. +            while (isdigit(*cp)) {
  1092. +            max_size *= 10;
  1093. +            max_size += *cp - '0';
  1094. +            ++cp;
  1095. +            }
  1096. +            smtpb->smtp_flags |= ESMTP_size;
  1097. +            smtpb->max_size = max_size;
  1098. +        } else if (strncmpic(cp, "8BITMIME", keywordlength) == 0) {
  1099. +            smtpb->smtp_flags |= ESMTP_8bitmime;
  1100. +        } else if (strncmpic(cp, "PIPELINING", keywordlength) == 0) {
  1101. +            smtpb->smtp_flags |= ESMTP_pipelining;
  1102. +        } else if (strncmpic(cp, "XVRB", keywordlength) == 0) {
  1103. +            smtpb->smtp_flags |= ESMTP_verbose;
  1104. +        } else if (strncmpic(cp, "XONE", keywordlength) == 0) {
  1105. +            smtpb->smtp_flags |= ESMTP_one;
  1106. +        } else if (strncmpic(cp, "XQUE", keywordlength) == 0) {
  1107. +            smtpb->smtp_flags |= ESMTP_queue;
  1108. +        } else {
  1109. +        }
  1110. +          skip_rest_of_line:
  1111. +        while (*cp != 0 && *cp != '\n')
  1112. +            ++cp;
  1113. +        if (*cp == '\n')
  1114. +            ++cp;
  1115. +        }
  1116. +#ifndef NO_LOG_EHLO
  1117. +        write_log(LOG_SYS, "destination supports esmtp%s%s%s%s%s%s",
  1118. +              smtpb->smtp_flags & ESMTP_8bitmime ? " 8BITMIME" : "",
  1119. +              smtpb->smtp_flags & ESMTP_size ? " SIZE" : "",
  1120. +              smtpb->smtp_flags & ESMTP_pipelining ? " PIPELINING" : "",
  1121. +              smtpb->smtp_flags & ESMTP_verbose ? " XVRB" : "",
  1122. +              smtpb->smtp_flags & ESMTP_one ? " XONE" : "",
  1123. +              smtpb->smtp_flags & ESMTP_queue ? " XQUE" : "");
  1124. +#endif /* not NO_LOG_EHLO */
  1125. +        if (smtpb->smtp_flags & ESMTP_verbose
  1126. +        && debug >= DBG_DRIVER_MID) {
  1127. +        smtp_out.i = 0;
  1128. +        (void) str_printf(&smtp_out, VERB);
  1129. +        reply = wait_write_command(smtpb, smtpb->short_timeout,
  1130. +                       smtp_out.p, smtp_out.i, &reply_text);
  1131. +        if (REPLY_GROUP (reply) == NEGATIVE_TRY_AGAIN) {
  1132. +            /* remote SMTP closed, try again later */
  1133. +            *error_p = try_again(smtpb->tp, reply_text);
  1134. +            return SMTP_AGAIN;
  1135. +        }
  1136. +        }
  1137. +        return SMTP_SUCCEED;
  1138. +      malformed_ehlo_reply:
  1139. +        /* This seems to be a reasonable way
  1140. +           to handle malformed EHLO replies: */
  1141. +#ifndef NO_LOG_EHLO
  1142. +        write_log(LOG_SYS, "destination supports esmtp, but is buggy (%s)",
  1143. +              reply_text);
  1144. +#endif /* not NO_LOG_EHLO */
  1145. +        return SMTP_SUCCEED;
  1146. +    }
  1147. +    }
  1148. +#endif /* HAVE_EHLO */
  1149.      /*
  1150.       * say who we are.
  1151.       * Possible responses:
  1152. @@ -141,6 +315,9 @@
  1153.       *    421 - closing down  (try again later)
  1154.       *  5xx - fatal error   (return message to sender)
  1155.       */
  1156. +#ifdef HAVE_EHLO
  1157. +  try_helo:
  1158. +#endif
  1159.      smtp_out.i = 0;
  1160.      (void) str_printf(&smtp_out, HELO(primary_name));
  1161.  
  1162. @@ -152,10 +329,47 @@
  1163.      *error_p = try_again(smtpb->tp, reply_text);
  1164.      return SMTP_AGAIN;
  1165.      }
  1166. -    if (reply != REPLY_OK) {
  1167. +    if (reply == REPLY_SEQUENCE_ERROR) {
  1168. +    /* ignore 503 Bad sequence of commands after EHLO/RSET/HELO */
  1169. +#ifndef NO_LOG_EHLO
  1170. +    write_log(LOG_SYS, "503 after EHLO/RSET/HELO (%s)",
  1171. +          reply_text);
  1172. +#endif /* not NO_LOG_EHLO */
  1173. +    } else if (reply != REPLY_OK) {
  1174. +#ifdef HAVE_EHLO
  1175. +    if (! tried_rset) {
  1176. +        /* The following */
  1177. +        /* fatal error, probably doesn't understand EHLO */
  1178. +        smtp_out.i = 0;
  1179. +        (void) str_printf(&smtp_out, "RSET");
  1180. +
  1181. +        reply = wait_write_command(smtpb, smtpb->short_timeout,
  1182. +                       smtp_out.p, smtp_out.i, &reply_text);
  1183. +        if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
  1184. +        /* remote SMTP closed, try again later */
  1185. +        *error_p = try_again(smtpb->tp, reply_text);
  1186. +        return SMTP_AGAIN;
  1187. +        } else if (reply == REPLY_OK) {
  1188. +        } else {
  1189. +        /* Some gateways don't accept any commands before they get
  1190. +           a HELO, not even RSET.  Fortunately it is usually safe
  1191. +           to ignore the error messages. */
  1192. +#ifndef NO_LOG_EHLO
  1193. +        write_log(LOG_SYS, "unexpected response to RSET (%s)",
  1194. +              reply_text);
  1195. +#endif /* not NO_LOG_EHLO */
  1196. +        }
  1197. +        /* the RSET command has been accepted; try again with HELO */
  1198. +        tried_rset = 1;
  1199. +        goto try_helo;    /* GASP!!! */
  1200. +    } else {
  1201. +#endif /* HAVE_EHLO */
  1202.      /* fatal error, return message to sender */
  1203.      *error_p = fatal_error(smtpb->tp, reply_text);
  1204.      return SMTP_FAIL;
  1205. +#ifdef HAVE_EHLO
  1206. +    }
  1207. +#endif
  1208.      }
  1209.  
  1210.      /* connection established */
  1211. @@ -221,11 +435,36 @@
  1212.          get_sender_addr(tp)),
  1213.             MAIL_END);
  1214.  
  1215. -    reply = wait_write_command(smtpb, smtpb->long_timeout,
  1216. +    /*
  1217. +     * send (a guess of) the size of the message to be transported.
  1218. +     * Of course the guess could be a bit more educated, but usually
  1219. +     * it doesn't matter if it is slightly incorrect.  We simply add
  1220. +     * 2% in order to account for \n -> \r\n conversion.
  1221. +     */
  1222. +    if (smtpb->smtp_flags & ESMTP_size) {
  1223. +    str_printf(&smtp_out, " SIZE=%lu",
  1224. +           (unsigned long) (msg_size * 1.02));
  1225. +    }
  1226. +#if 0
  1227. +    /*
  1228. +     * This is commented out because we are not supposed to send a
  1229. +     * non-MIME message on 8BITMIME mode.  But sending a non-7bit
  1230. +     * clean message in 7BIT mode isn't a good idea either.  Sigh.
  1231. +     * Something more sophisticated is definitely needed here.
  1232. +     */
  1233. +    if (smtpb->smtp_flags & ESMTP_8bitmime) {
  1234. +    str_printf(&smtp_out, " BODY 8BITMIME");
  1235. +    }
  1236. +#endif
  1237. +
  1238. +    /* give all of the recipient addresses to the remote SMTP */
  1239. +    okay = NULL;
  1240. +
  1241. +    reply = write_command_maybe_wait(smtpb, smtpb->long_timeout,
  1242.                     smtp_out.p, smtp_out.i, &reply_text);
  1243.  
  1244.      if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
  1245. -    *error_p = try_again(smtpb->tp, reply_text);
  1246. +    *error_p = try_again(tp, reply_text);
  1247.      insert_addr_list(addr, defer, *error_p);
  1248.      do_smtp_shutdown(smtpb, reply);
  1249.      return FAIL;
  1250. @@ -253,15 +492,15 @@
  1251.  
  1252.      str_printf(&smtp_out, "%s%s%s", RCPT_BEGIN, cur->next_addr, RCPT_END);
  1253.  
  1254. -    reply = wait_write_command(smtpb, smtpb->long_timeout,
  1255. +    reply = write_command_maybe_wait(smtpb, smtpb->long_timeout,
  1256.                     smtp_out.p, smtp_out.i, &reply_text);
  1257.  
  1258. -    if (reply == REPLY_STORAGE_FULL
  1259. -        || REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN)
  1260. -    {
  1261. -        *error_p = (reply == REPLY_STORAGE_FULL)?
  1262. -        remote_full(tp, reply_text):
  1263. -        try_again(smtpb->tp, reply_text);
  1264. +    if (reply == REPLY_STORAGE_FULL) {
  1265. +        *error_p = remote_full(tp, reply_text);
  1266. +        insert_addr_list(cur, defer, *error_p);
  1267. +        next = NULL;
  1268. +    } else if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
  1269. +        *error_p = try_again(tp, reply_text);
  1270.          insert_addr_list(cur, defer, *error_p);
  1271.          insert_addr_list(okay, defer, *error_p);
  1272.          do_smtp_shutdown(smtpb, reply);
  1273. @@ -285,9 +524,70 @@
  1274.       *    4xx - remote error       (try all recipients again later)
  1275.       *  5xx - fatal error        (return message to sender)
  1276.       */
  1277. -    reply = wait_write_command(smtpb, smtpb->long_timeout,
  1278. +    reply = write_command_maybe_wait(smtpb, smtpb->long_timeout,
  1279.                     DATA, sizeof(DATA) - 1, &reply_text);
  1280.  
  1281. +    if (smtpb->smtp_flags & ESMTP_pipelining) {
  1282. +
  1283. +    if (reply != REPLY_OK) {
  1284. +        *error_p = try_again(tp, reply_text);
  1285. +        insert_addr_list(addr, defer, *error_p);
  1286. +        do_smtp_shutdown(smtpb, reply);
  1287. +        return FAIL;
  1288. +    }
  1289. +
  1290. +    if (flush_command_stream(smtpb, &reply_text) != REPLY_OK) {
  1291. +        return FAIL;
  1292. +    }
  1293. +
  1294. +    /* Now read all the responses.  First the MAIL FROM: reply */
  1295. +    reply = wait_read_response(smtpb, smtpb->long_timeout,
  1296. +                   &reply_text);
  1297. +    if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
  1298. +        *error_p = try_again(tp, reply_text);
  1299. +        insert_addr_list(addr, defer, *error_p);
  1300. +        do_smtp_shutdown(smtpb, reply);
  1301. +        return FAIL;
  1302. +    } else if (REPLY_GROUP(reply) == NEGATIVE_FAILED) {
  1303. +        *error_p = fatal_error(tp, reply_text);
  1304. +        insert_addr_list(addr, fail, *error_p);
  1305. +        do_smtp_shutdown(smtpb, reply);
  1306. +        return FAIL;
  1307. +    }
  1308. +
  1309. +    addr = okay;
  1310. +    okay = NULL;
  1311. +
  1312. +    for (cur = addr; cur; cur = next) {
  1313. +        next = cur->succ;
  1314. +
  1315. +        reply = wait_read_response(smtpb, smtpb->long_timeout,
  1316. +                       &reply_text);
  1317. +
  1318. +        if (reply == REPLY_STORAGE_FULL) {
  1319. +        *error_p = remote_full(tp, reply_text);
  1320. +        insert_addr_list(cur, defer, *error_p);
  1321. +        next = NULL;
  1322. +        } else if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
  1323. +        *error_p = try_again(tp, reply_text);
  1324. +        insert_addr_list(cur, defer, *error_p);
  1325. +        insert_addr_list(okay, defer, *error_p);
  1326. +        do_smtp_shutdown(smtpb, reply);
  1327. +        return FAIL;
  1328. +        } else if (REPLY_GROUP(reply) == NEGATIVE_FAILED) {
  1329. +        cur->error =  remote_bad_address(tp, reply_text);
  1330. +        cur->succ = *fail;
  1331. +        *fail = cur;
  1332. +        } else {
  1333. +        /* successful thus far */
  1334. +        cur->succ = okay;
  1335. +        okay = cur;
  1336. +        }
  1337. +    }
  1338. +    reply = wait_read_response(smtpb, smtpb->long_timeout,
  1339. +                   &reply_text);
  1340. +    }
  1341. +
  1342.      if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
  1343.      *error_p = try_again(tp, reply_text);
  1344.      insert_addr_list(okay, defer, *error_p);
  1345. @@ -306,7 +606,7 @@
  1346.      /*
  1347.       * send the message, using the hidden dot protocol.
  1348.       */
  1349. -    smtpb->tp->flags |= PUT_DOTS;
  1350. +    tp->flags |= PUT_DOTS;
  1351.      success = write_message(smtpb->out, tp, addr);
  1352.  
  1353.      if (success == WRITE_FAIL) {
  1354. @@ -381,6 +681,21 @@
  1355.      }
  1356.  }
  1357.  
  1358. +static int
  1359. +write_command_maybe_wait (smtpb, timeout, text, len, reply_text)
  1360. +    struct smtp *smtpb;            /* SMTP description block */
  1361. +    unsigned timeout;            /* read timeout */
  1362. +    char *text;                /* text of command */
  1363. +    register int len;            /* length of command */
  1364. +    char **reply_text;            /* text of response from remote */
  1365. +{
  1366. +    if (smtpb->smtp_flags & ESMTP_pipelining) {
  1367. +    return write_command_nowait (smtpb, text, len);
  1368. +    } else {
  1369. +    return wait_write_command (smtpb, timeout, text, len, reply_text);
  1370. +    }
  1371. +}
  1372. +
  1373.  /*
  1374.   * wait_write_command - send a command, then wait for the response
  1375.   *
  1376. @@ -395,6 +710,30 @@
  1377.      register int len;            /* length of command */
  1378.      char **reply_text;            /* text of response from remote */
  1379.  {
  1380. +    int reply;
  1381. +
  1382. +    reply = write_command_nowait (smtpb, text, len);
  1383. +    if (reply != REPLY_OK)
  1384. +    return reply;
  1385. +
  1386. +    reply = flush_command_stream (smtpb, reply_text);
  1387. +    if (reply != REPLY_OK)
  1388. +    return reply;
  1389. +
  1390. +    /* wait for the response to come back */
  1391. +    reply = wait_read_response(smtpb, timeout, reply_text);
  1392. +    return reply;
  1393. +}
  1394. +
  1395. +/*
  1396. + * write_command_nowait - send a command
  1397. + */
  1398. +static int
  1399. +write_command_nowait(smtpb, text, len)
  1400. +    struct smtp *smtpb;            /* SMTP description block */
  1401. +    char *text;                /* text of command */
  1402. +    register int len;            /* length of command */
  1403. +{
  1404.      register FILE *f = smtpb->out;
  1405.      register char *cp;
  1406.      int reply;
  1407. @@ -421,19 +760,22 @@
  1408.      for (cp = smtpb->nl; *cp; cp++) {
  1409.      putc(*cp, f);
  1410.      }
  1411. +    return REPLY_OK;
  1412. +}
  1413. +
  1414. +static int
  1415. +flush_command_stream(smtpb, reply_text)
  1416. +    struct smtp *smtpb;            /* SMTP description block */
  1417. +    char **reply_text;            /* text of response from remote */
  1418. +{
  1419. +    register FILE *f = smtpb->out;
  1420. +
  1421.      (void) fflush(f);
  1422.      if (ferror(f)) {
  1423.      *reply_text = "499 write error, remote probably down";
  1424.      DEBUG1(DBG_DRIVER_MID, "SMTP-reply: %s\n", *reply_text);
  1425.      return REPLY_TIMEOUT;
  1426.      }
  1427. -
  1428. -    /* wait for the response to come back */
  1429. -    if (smtpb->in) {
  1430. -    reply = wait_read_response(smtpb, timeout, reply_text);
  1431. -    DEBUG1(DBG_DRIVER_MID, "SMTP-reply: %s\n", *reply_text);
  1432. -    return reply;
  1433. -    }
  1434.      return REPLY_OK;
  1435.  }
  1436.  
  1437. @@ -445,6 +787,27 @@
  1438.   */
  1439.  static int
  1440.  wait_read_response(smtpb, timeout, reply_text)
  1441. +    struct smtp *smtpb;            /* SMTP description block */
  1442. +    unsigned timeout;            /* read timeout */
  1443. +    char **reply_text;            /* return text of response here */
  1444. +{
  1445. +    int result;
  1446. +
  1447. +    /* If we're in batch mode, always return success. */
  1448. +    if (! smtpb->in) {
  1449. +    return REPLY_OK;
  1450. +    }
  1451. +    do
  1452. +      {
  1453. +    result = read_response_internal (smtpb, timeout, reply_text);
  1454. +    DEBUG1(DBG_DRIVER_MID, "SMTP-reply: %s\n", *reply_text);
  1455. +      }
  1456. +    while (REPLY_GROUP (result) == POSITIVE_DEBUG);
  1457. +    return result;
  1458. +}
  1459. +
  1460. +static int
  1461. +read_response_internal(smtpb, timeout, reply_text)
  1462.      struct smtp *smtpb;            /* SMTP description block */
  1463.      unsigned timeout;            /* read timeout */
  1464.      char **reply_text;            /* return text of response here */
  1465. --- src/transports/smtplib.h    1994/11/14 09:46:49    1.1
  1466. +++ src/transports/smtplib.h    1994/12/13 10:59:54
  1467. @@ -1,4 +1,4 @@
  1468. -/* @(#) $Id: smtplib.h,v 1.1 1994/11/14 09:46:49 logiciel Exp $ */
  1469. +/* @(#) $Id: smtplib.h,v 1.4 1994/12/13 10:59:53 logiciel Exp $ */
  1470.  
  1471.  /*
  1472.   *    Copyright (C) 1987, 1988 by Ronald S. Karr and Landon Curt Noll
  1473. @@ -17,6 +17,20 @@
  1474.  #define SMTP_SUCCEED    0
  1475.  #define SMTP_FAIL    (-1)
  1476.  #define SMTP_AGAIN    (-2)
  1477. +#define SMTP_EHLO_FAIL    (-3)
  1478. +
  1479. +typedef enum
  1480. +{
  1481. +    ESMTP_none        = 0x0000,
  1482. +    ESMTP_basic        = 0x0001,
  1483. +    ESMTP_8bitmime    = 0x0002,
  1484. +    ESMTP_size        = 0x0004,
  1485. +    ESMTP_pipelining    = 0x0008,
  1486. +    ESMTP_verbose    = 0x0010,
  1487. +    ESMTP_one        = 0x0020,
  1488. +    ESMTP_queue        = 0x0040
  1489. +}
  1490. +SMTPExtension, SMTPExtensionFlags;
  1491.  
  1492.  /*
  1493.   * the following structure is passed around between SMTP functions and
  1494. @@ -32,6 +46,8 @@
  1495.      unsigned long_timeout;        /* normal SMTP read timeout period */
  1496.      char *nl;                /* line terminator string */
  1497.      struct transport *tp;        /* associated transport */
  1498. +    SMTPExtensionFlags smtp_flags;    /* SMTP extensions supported by remote */
  1499. +    unsigned long max_size;        /* message size limit of remote */
  1500.  };
  1501.  
  1502.  /* functions defined in smtplib.c */
  1503. --- src/transports/tcpsmtp.c    1994/11/14 09:47:25    1.1
  1504. +++ src/transports/tcpsmtp.c    1994/12/13 11:29:15
  1505. @@ -288,6 +288,7 @@
  1506.      char *error_text;
  1507.      int success;
  1508.      struct addr *ap;
  1509. +    int try_ehlo;
  1510.  
  1511.      priv = (struct tcpsmtp_private *)tp->private;
  1512.  
  1513. @@ -310,41 +311,53 @@
  1514.  
  1515.      /* reach out and touch someone */
  1516.  
  1517. -    s = tcpsmtp_connect(hostname, ipaddr, family, service, &error_text);
  1518. -    if (s >= 0) {
  1519. -        s2 = dup(s);
  1520. -        if (s2 < 0) {
  1521. -            (void) close(s);
  1522. -            s = -1;
  1523. -        }
  1524. -    }
  1525. -    if (s < 0) {
  1526. -        *ep = connect_failure(tp, error_text);
  1527. -        return SMTP_AGAIN;
  1528. -    }
  1529. -
  1530. -    smtpbuf.in = fdopen(s, "r");
  1531. -    smtpbuf.out = fdopen(s2, "w");
  1532. -    smtpbuf.short_timeout = priv->short_timeout;
  1533. -    smtpbuf.long_timeout = priv->long_timeout;
  1534. -    smtpbuf.nl = "\r\n";
  1535. -    tp->flags |= PUT_CRLF;
  1536. -    smtpbuf.tp = tp;
  1537. -
  1538. -    DEBUG(DBG_DRIVER_LO, "connected\n");
  1539. -
  1540. -    switch (smtp_startup(&smtpbuf, ep)) {
  1541. -
  1542. -    case SMTP_FAIL:
  1543. -        insert_addr_list(addr, fail, *ep);
  1544. -        (void) fclose(smtpbuf.in);
  1545. -        (void) fclose(smtpbuf.out);
  1546. -        return SMTP_FAIL;
  1547. -
  1548. -    case SMTP_AGAIN:
  1549. -        (void) fclose(smtpbuf.in);
  1550. -        (void) fclose(smtpbuf.out);
  1551. -        return SMTP_AGAIN;
  1552. +    for (try_ehlo = 1, success = 0; !success && try_ehlo >= 0; --try_ehlo) {
  1553. +    s = tcpsmtp_connect(hostname, ipaddr, family, service, &error_text);
  1554. +    if (s >= 0) {
  1555. +        s2 = dup(s);
  1556. +        if (s2 < 0) {
  1557. +        (void) close(s);
  1558. +        s = -1;
  1559. +        }
  1560. +    }
  1561. +    if (s < 0) {
  1562. +        *ep = connect_failure(tp, error_text);
  1563. +        return SMTP_AGAIN;
  1564. +    }
  1565. +
  1566. +    smtpbuf.in = fdopen(s, "r");
  1567. +    smtpbuf.out = fdopen(s2, "w");
  1568. +    smtpbuf.short_timeout = priv->short_timeout;
  1569. +    smtpbuf.long_timeout = priv->long_timeout;
  1570. +    smtpbuf.nl = "\r\n";
  1571. +    tp->flags |= PUT_CRLF;
  1572. +    smtpbuf.tp = tp;
  1573. +    smtpbuf.smtp_flags = ESMTP_none;
  1574. +    smtpbuf.max_size = 0;
  1575. +
  1576. +    DEBUG(DBG_DRIVER_LO, "connected\n");
  1577. +
  1578. +    switch (smtp_startup(&smtpbuf, ep, try_ehlo)) {
  1579. +
  1580. +      case SMTP_FAIL:
  1581. +        insert_addr_list(addr, fail, *ep);
  1582. +        (void) fclose(smtpbuf.in);
  1583. +        (void) fclose(smtpbuf.out);
  1584. +        return SMTP_FAIL;
  1585. +
  1586. +      case SMTP_AGAIN:
  1587. +        (void) fclose(smtpbuf.in);
  1588. +        (void) fclose(smtpbuf.out);
  1589. +        return SMTP_AGAIN;
  1590. +
  1591. +      case SMTP_EHLO_FAIL:
  1592. +        (void) fclose(smtpbuf.in);
  1593. +        (void) fclose(smtpbuf.out);
  1594. +        break;
  1595. +
  1596. +      default:
  1597. +        success = 1;
  1598. +    }
  1599.      }
  1600.  
  1601.      if (dont_deliver) {
  1602. --- /dev/null    Tue Dec 13 15:30:59 1994
  1603. +++ src/geniobpeek.sh    Mon Nov 14 10:48:56 1994
  1604. @@ -0,0 +1,81 @@
  1605. +#!/bin/sh
  1606. +#
  1607. +# This script tries to generate a file "iobpeek.h" which defines one
  1608. +# macro, IOB_MAYBE_EMPTY_P.  The macro can be called with one
  1609. +# argument, a stdio input stream, and must return a non-zero value if
  1610. +# the stream's buffer is empty.  If there are characters in the input
  1611. +# buffer, it *should* return zero.
  1612. +
  1613. +# work around brain-damaged Ultrix shell
  1614. +if test -z "$foo" -a -r /bin/sh5
  1615. +then
  1616. +  export foo; foo=bar; exec /bin/sh5 $0 "$1" "$2"
  1617. +fi
  1618. +
  1619. +CC=$1; shift
  1620. +CFLAGS=$1; shift
  1621. +ECHO=echo
  1622. +#ECHO=:
  1623. +
  1624. +rm -f ,testfile
  1625. +
  1626. +try ()
  1627. +{
  1628. +  echo foo bar > ,testfile
  1629. +  empty_test="$1"
  1630. +  $ECHO "Trying $empty_test"
  1631. +  rm -f ,iobtest.c
  1632. +  cat > ,iobtest.c <<EOM
  1633. +#include <stdio.h>
  1634. +
  1635. +#define IOB_MAYBE_EMPTY_P(stream) $empty_test
  1636. +
  1637. +main (argc, argv) int argc; char **argv;
  1638. +{
  1639. +  FILE * in = fopen(",testfile", "r");
  1640. +  int c;
  1641. +  if (! in) { fprintf (stderr, "Couldn't open test file\n"); exit (1); }
  1642. +  if (! IOB_MAYBE_EMPTY_P (in)) {
  1643. +    fprintf (stderr, "Buffer not empty after fopen()\n"); exit (1);
  1644. +  }
  1645. +  c = getc(in);
  1646. +  if (c != 'f') { fprintf (stderr, "Read error\n"); exit (1); }
  1647. +  if (IOB_MAYBE_EMPTY_P (in)) {
  1648. +    fprintf (stderr, "Buffer empty after reading one char\n"); exit (1);
  1649. +  }
  1650. +  return 0;
  1651. +}
  1652. +EOM
  1653. +
  1654. +  $CC $CFLAGS -o ,iobtest ,iobtest.c >/dev/null 2>&1 || \
  1655. +    ( $ECHO "  compilation failed"; return 1 )
  1656. +  if ./,iobtest
  1657. +  then
  1658. +    cat > iobpeek.h <<EOM
  1659. +/* iobpeek.h - DO NOT EDIT!
  1660. +   Automatically generated by geniobpeek.sh */
  1661. +#define IOB_MAYBE_EMPTY_P(stream) $empty_test
  1662. +EOM
  1663. +    status=0
  1664. +  else
  1665. +    status=1
  1666. +  fi
  1667. +  rm ,iobtest ,iobtest.c ,testfile
  1668. +  return $status
  1669. +}
  1670. +
  1671. +# IRIX, SunOS 4, Solaris 2
  1672. +try '((stream)->_cnt == 0)' && exit 0
  1673. +# Older systems may have this
  1674. +try '((stream)->cnt == 0)' && exit 0
  1675. +# Linux
  1676. +try '((stream)->_IO_read_ptr >= (stream)->_IO_read_end)' && exit 0
  1677. +# GNU libc 1.08.1
  1678. +try '((stream)->bufp >= (stream)->__get_limit)' && exit 0
  1679. +
  1680. +cat 1>&2 <<EOM
  1681. +*** Error: could not find out how to check for an empty stdio stream
  1682. +***        buffer on this system.  Please notify Simon Leinen
  1683. +***        <simon@lia.di.epfl.ch> of this problem.
  1684. +EOM
  1685. +exit 1
  1686.